

/**
 *******************************************************************************
 * @file    tiny_kvdb.h
 * @brief   tiny key-value database header file.
 *******************************************************************************
 */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __TINY_KVDB_H
#define __TINY_KVDB_H

#ifdef __cplusplus
extern "C"
{
#endif

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include "tiny_kvdb_cfg.h"


/* Exported Defines ----------------------------------------------------------*/

#define TINY_DB_ERR_NONE                (0)
#define TINY_DB_ERR_NULL_POINTER        (-1)
#define TINY_DB_ERR_INVALID_PARAM       (-2)
#define TINY_DB_ERR_MAX_USER            (-3)
#define TINY_DB_ERR_NOT_INIT            (-4)
#define TINY_DB_ERR_IN_USE              (-5)
#define TINY_DB_ERR_NO_SPACE            (-6)
#define TINY_DB_ERR_NOT_FOUND           (-7)
#define TINY_DB_ERR_READ                (-8)
#define TINY_DB_ERR_WRITE               (-9)
#define TINY_DB_ERR_ERASE               (-10)

/* Exported Types ------------------------------------------------------------*/
typedef struct
{
    uint16_t    key;
    uint16_t    len;
    uint32_t    addr;
} tiny_kvdb_record_info_t;

typedef struct
{
    uint32_t    device_id;          /* unique device id in application, we can just use a magic word */
    uint32_t    start_addr;         /* physical start address of the device */
    uint32_t    max_size;           /* max size */
    uint16_t    sector_size;        /* physical sector size of the device */
    uint16_t    write_unit;         /* minimum write unit, 1, 2, 4 or 8 bytes */

    /* operation api */
    /* return >= 0, success; return < 0, error */
    int         (*init)(void);
    int         (*read)(uint32_t addr, void * p_dst, uint32_t len);     /* read bytes */
    int         (*write)(uint32_t addr, void * p_src, uint32_t len);    /* write bytes */
    int         (*erase)(uint32_t addr, uint32_t len);                  /* erase sectors */
} tiny_db_device_t;

typedef struct tiny_kvdb_s tiny_kvdb_t;
typedef void (*pfunc)(tiny_kvdb_t *db);

struct tiny_kvdb_s
{
    const tiny_db_device_t   *dev;
    uint32_t        data_addr;      /* data sector start address */
    uint32_t        swap_addr;      /* swap sector start address */
    uint32_t        next_addr;      /* next address to save data */
    uint32_t        total_size;     /* total size of the database */
    uint32_t        dirty_size;     /* dirty data length */
    uint32_t        init_ok;

    pfunc           lock;           /* lock the database */
    pfunc           unlock;         /* unlock the database */

#if TINY_KVDB_USE_CACHE
    tiny_kvdb_record_info_t cache[TINY_KVDB_CACHE_NUM];
#endif
};

/* Exported Macros -----------------------------------------------------------*/
#ifndef TINY_KVDB_PRINT
#if TINY_KVDB_LOG_EN
#if 0
    #include "segger_rtt.h"
    #define TINY_KVDB_PRINT(...)    SEGGER_RTT_printf(0, __VA_ARGS__)
#else
    #include <stdio.h>
    #define TINY_KVDB_PRINT         printf
#endif
#else
    #define TINY_KVDB_PRINT(...)
#endif
#endif

#if TINY_KVDB_DEBUG_EN
#define TINY_KVDB_DEBUG(...)        TINY_KVDB_PRINT("[debug] "__VA_ARGS__)
#else
#define TINY_KVDB_DEBUG(...)
#endif
#define TINY_KVDB_INFO(...)         TINY_KVDB_PRINT("[info] "__VA_ARGS__)
#define TINY_KVDB_ERROR(...)        TINY_KVDB_PRINT("[error] "__VA_ARGS__)

/* Exported Variables --------------------------------------------------------*/

/* Exported Functions --------------------------------------------------------*/
/**
 * @brief initialize a database.
 * @param db the database instance to be initalized.
 * @param dev the storage device that the database used.
 * @param start_addr start address of the database on the device.
 * @param size database size.
 * @return 0 if ok, <0 if fail.
 *
 * @note start_addr MUST BE the start address of a sector of the device.
 * @note size MUST BE a multiple of 2x the sector size of the device.
*/
int tiny_kvdb_init(tiny_kvdb_t *db, const tiny_db_device_t *dev, uint32_t start_addr, uint32_t size);

/**
 * @brief delete a key-value from a database.
 * @param db the database instance.
 * @param key the key to be deleted.
 * @return 0 if ok, <0 if fail.
*/
int tiny_kvdb_delete(tiny_kvdb_t *db, uint16_t key);

/**
 * @brief read a key-value from a database.
 * @param db the database instance.
 * @param key the key to be read.
 * @param p_dst dest buffer to save the value.
 * @param len expected length to be read.
 * @return >0 if ok, <0 if fail.
 *
 * @note if p_dst is NULL or len = 0, will just return the key-value length saved in the database.
*/
int tiny_kvdb_read(tiny_kvdb_t *db, uint16_t key, void *p_dst, uint16_t len);

/**
 * @brief write a key-value to a database.
 * @param db the database instance.
 * @param key the key to be written.
 * @param p_src source buffer that saved the value.
 * @param len expected length to be written.
 * @return >0 if ok, <0 if fail.
 *
 * @note if p_dst is NULL or len = 0, will delete the key-value.
*/
int tiny_kvdb_write(tiny_kvdb_t *db, uint16_t key, void *p_src, uint16_t len);

/**
 * @brief do garbage collection of a database.
 * @param db the database instance.
 * @return 0 if ok, <0 if fail.
*/
int tiny_kvdb_gc(tiny_kvdb_t *db);

/**
 * @brief clear all key-value and format a database.
 * @param db the database instance.
 * @return 0 if ok, <0 if fail.
*/
int tiny_kvdb_format(tiny_kvdb_t *db);

/**
 * @brief get free space of a database.
 * @param db the database instance.
 * @return >0 if ok, <0 if fail.
*/
int tiny_kvdb_get_free_space(tiny_kvdb_t *db);

/**
 * @brief set the lock and unlock functions of a database.
 * @param db the database instance.
 * @return 0 if ok, <0 if fail.
*/
int tiny_kvdb_set_pfunc(tiny_kvdb_t *db, pfunc lock, pfunc unlock);

#ifdef __cplusplus
}
#endif

#endif

/******************************** END OF FILE *********************************/




